fix: chunk Demucs inference and stop boot-time memory spikes - #4
Merged
Conversation
- Replace whole-song HDemucs inference with overlapping ~8s chunked inference (_demucs_chunked_inference). Peak memory for a 5-min track drops from 15+ GB to ~2.5 GB; quality is identical as this matches the original Demucs library's default segmented approach. - Fix recover_incomplete_attempts() to mark stale running jobs as 'interrupted' instead of requeueing them as 'queued'. Previously, any measurement or symbolic attempt stuck as 'running' at shutdown would auto-fire a heavy ML subprocess on every server boot. - Update test_recover_interrupted_attempts to match new interrupted semantics for measurement and symbolic stages. - Remove committed runtime backups and broken venv (~5.7 GB): .runtime.backup.20260319-* (3 dirs), venv.broken.20260319-153925 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
May 13, 2026
…g completion (#18) * feat: Phase 1.A–1.D depth additions, Track 2 audit, decision-gate v3 + catalog completion Substantial measurement-depth pass on the ASA Phase 1 pipeline plus a trustworthy multi-model decision-gate harness. Default Gemini model (gemini-2.5-flash) now PASSes the stem-aware gate on the Vtss bench track; Live 12 catalog completion drops UNKNOWN_PARAMETER 16 → 1 across all v3.1 snapshots. ## Depth additions (Phase 1.A – 1.D) - Phase 1.A cheap wins (5): per-frame short-term LUFS curve, per-frame 7-band spectral balance time series, instantaneous tempoCurve, per-second stereo correlationCurve, 256-point arrangement noveltyCurve (up from 64). - Phase 1.B stem-first refactor: per-stem subtree under ``stemAnalysis.{drums,bass,other,vocals}`` with the same shape as the full-mix analyzers. Phase 2 can now cite element-specific measurements. - Phase 1.C #0 real time-signature detection (onset-accent autocorrelation, replaces the always-4/4 fallback). - Phase 1.C #1 per-band transient density across the 7 spectralBalance bands (kick / hi-hat density anchors). - Phase 1.C #2 per-band stereo correlations (Utility-width-per-band recommendations). - Phase 1.C #3 ``grooveDetail.perDrumSwing.{kick,snare,hihat}`` from the mid-band beat-loudness signal. - Phase 1.C #4 snareDetail / hihatDetail band-limited drum character (hits, attack sharpness, body/snap energy ratio, decay). - Phase 1.C #5 saturationDetail (clipped sample count, peakRatio95to50, rmsToPeakRatioDb, saturationLikely). - Phase 1.C #6 32nd-note sidechain resolution + envelopeShape32 (legacy envelopeShape preserved via max-pairing downsample); pumpingRate now four-valued including thirty_second. - Phase 1.D #5 RT60 reverb per stem with perBandRt60 (low / lowMids / highMids / highs) + preDelayMs via Schroeder-equivalent slope fit. Wired through stemAnalysis.{stem}.reverbDetail. ## Track 2 audit pass 1 + measurement-quality fixes - New ``scripts/audit_pass1.py`` harness — structured 12-section markdown audit comparing real-track Phase 1 measurements against declared ground truth and sanity rules. - Bug fix: ``bassDetail.averageDecayMs`` was 0 ms on real bass material (Codex PDF finding reproduced). Root cause: decay loop searched from onset rather than peak, and checked the raw oscillating waveform rather than an envelope. Fixed with a 20 ms RMS envelope + peak-anchored search. Synthetic validation: 350 ms theoretical → 346 ms measured; 35 ms → 37 ms. Real-track Vtss: 0 ms (FAIL) → 66–125 ms (PASS). - Bug fix: ``vocalDetail.hasVocals = true`` false positive on instrumental synth content. Added temporal-formant-stability check (sustained synth leads have near-static "formants"; real vocals shift formants 100+ Hz with syllables); tightened formant tolerance 200 → 100 Hz; raised threshold 0.45 → 0.55. No-stem run on Vtss now correctly reports hasVocals=false at conf 0.48. - Pre-existing import bugs in ``analyze_segments.py``: ``_compute_stereo_metrics`` (from dsp_utils) and ``Counter`` (from collections) were undefined. ``segmentStereo`` + ``chordDetail`` now populate correctly. ## Phase 2 contract + validator + decision gate - Phase 2 prompt v3: per-stem path enumeration, citation contract (``phase1Fields`` required on every recommendation), "Phase-2 self-reference is not a citation" rule, dedicated sections for snare/hi-hat character, saturation, sidechain envelope, reverb detail. - Phase 2 schema (``server_phase2.py``): ``phase1Fields`` array required on ``mixAndMasterChain``, ``abletonRecommendations``, and ``secretSauce.workflowSteps`` items. - Frontend validator (``phase2Validator.ts``) overhaul: * 8 violation types (NUMERIC_OVERRIDE, GENRE_IGNORES_DSP, BOUNDS_VIOLATION, MISSING_CITATION, TRIVIAL_CITATIONS, NEW_FIELD_UNCITED, LOW_CONFIDENCE_NOT_HEDGED, RECOMMENDATION_SALVAGED). * Bidirectional + wildcard ``pathCoversTracked`` matcher so leaf citations satisfy parent tracked paths and ``stemAnalysis.*`` wildcards work. * 19 tracked Phase 1.A + Phase 1.C/D field paths (was 7). - Decision-gate vitests (4): single-, multi-, real-, and stem-aware multi-model comparators that read ``/tmp/decision_gate_*.json`` snapshots and emit per-model markdown reports. ## Live 12 catalog completion (UNKNOWN_PARAMETER closure) - Auto Filter: ``parameterAliases`` flat-map ``{"Filter Resonance": "Resonance", "Filter Frequency": "Frequency"}``. Closes the instrument-side long-form naming bleed. - Glue Compressor: ``allowedParameters`` expanded by 4 (Range, Sidechain, Sidechain Gain, Sidechain Dry/Wet). - ``_validate_phase2_catalog_entry`` resolves aliases before the membership check; startup-time loader validates the new ``parameterAliases`` shape. - New ``Phase2CatalogValidationTests`` (11 cases) covers alias resolution, expansion, scoping per-device, and negative cases. - New ``scripts/replay_catalog_validation.py`` — no-Gemini-spend harness that re-validates v3.1 snapshots against the live catalog. Verdict: UNKNOWN_PARAMETER drops 16 → 1 across all 8 snapshots; the remaining hit is ``EQ Eight / "Band 8 Filter Type"`` (separate device, v3.2-target). ## Test state - Backend: 374 tests pass (was 363; +11 ``Phase2CatalogValidationTests``). - Frontend: 296 tests pass (was 286; +10 validator bidirectional / wildcard tests). - UI lint clean (``tsc --noEmit``). ## Out of scope / follow-ups (documented in SESSION_BLITZ + plan) - Pro-model citation-depth iteration (3-pro and 3.1-pro still cite far less Phase 1.C/D than 2.5-flash). - gemini-3.1-pro-preview ``DROPPED_INVALID_ARRAY_ITEM`` salvage regression seen in earlier v3 runs. - Compressor "Sustain", "Ableton Project Settings", "Mixer" device hits — v3.2 prompt-fix targets. - Phase 1.D #1 madmom integration (prototype proven, install path documented at ``.runtime/reports/madmom_install_prototype_2026-05-12.md``). - Phase 1.D #2 chord progression deeper labelling. - ``JSON_SCHEMA.md`` documentation of the catalog schema (``parameterAliases`` field). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(backend): add coverage for Phase 1.C analyzers, per-stem orchestrator, and dsp_utils (#19) Closes the test-coverage gaps flagged in the PR review. Backend: - test_analyze.py: new BandDrumDetailTests, SnareDetailTests, HihatDetailTests, TransientDensityDetailTests, SaturationDetailTests, RunPerStemAnalysesTests. Each covers null/short/silent input guards, schema-field presence, and at least one behavioral assertion against a synthetic signal that should trip the detector. RunPerStemAnalysesTests covers stems=None, empty dict, all loads failing, mono+stereo success, partial-analyzer failure, and the stereo-load-skipped path. - BassDetailTests: regression test for averageDecayMs > 30 ms on a synthetic decaying-pulse signal — guards the envelope-based fix from sliding back to the pre-fix sub-millisecond range. - test_dsp_utils.py (new): direct tests for _pearson_corr, _downsample_lufs_array, _downsample_band_energies_curve, _compute_tempo_curve_from_ticks, and _compute_stereo_correlation_curve, with closed-form expected values for perfect/anti/orthogonal correlation, constant-input NaN handling, tempo-change detection, and the silent-sub None policy. Docs / comments: - JSON_SCHEMA.md: flag bassDetail.fundamentalHz as a ZCR approximation that biases upward on harmonic-rich basses; steer Phase 2 to prefer pitchDetail when stems are available and avoid narrow filter-Q moves within ±15 Hz of the value. - phase2Validator.ts: rewrite the pathCoversTracked docstring so it matches what the code actually does (a wildcard token covers all longer citation paths under the same prefix; "stemAnalysis.*" does match "stemAnalysis.drums.spectralBalance"). Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
May 16, 2026
…branch, store prune (#51) * audit: chain-of-custody, recommendations-first IA, applied tracker, grammar post-process Implements the design audit's prescription end-to-end (findings #1–#15 + N1/N2/N7/N9/N10 + follow-ups). The product's chain-of-custody promise — every Phase 2 recommendation traces back to the Phase 1 measurement that justifies it — is now visually first-class on every card, not a 9px monospace footnote. What ships: * Chain-of-custody (findings #2 + #3). New CitationBlock primitive renders a structured "GROUNDED IN" block above every Mix Chain / Patches / Sonic Element card with humanized labels (FIELD_LABELS map, ~50 entries + humanizeFieldPath fallback) and a ConfidenceBandBadge pill computed from the worst confidence among cited fields. Also retires GroundingBadgeList at the Track Layout site; segmentIndexes ride through a new extraRows prop. * Recommendations-first IA (#1). MeasurementDashboard moved to the bottom of the results scroll; StickyNav's 9 measurement pills collapse to one trailing "Measurements" entry. Producers hit Style → Sonic → Mix Chain → Patches → Session before the measurement evidence. * Header polish (#7, #9, #11). CPU meter removed (browser-tab CPU is misleading during backend analysis), "Local DSP Engine v1.6.0" eyebrow removed (resolves mobile 3-line wrap), Dense DAW Lab demoted from accent chip to quiet text link. * Engineering vocab cleanup (#8 + N3/N4/N5/N8). New userLabels.ts service translates field paths to producer-readable labels at every render. Button labels renamed (Download data / Download report). FAMILY: NATIVE chip dropped from meta-badge rows. workflowStage prettified at the view-model layer ("Sound design" not "SOUND_DESIGN"). AI Interpretation gated copy reworded ("AI interpretation isn't configured…" not "Developer kill-switch is off"). * Applied-recommendations tracker (#14 + #15). Per-card checkbox affordance + section- header "N of M applied" chip + localStorage persistence keyed by audio content SHA256. Producer can rename their file without losing their progress. * Idle value-prop panel (#5). Replaces the 200px "NO SIGNAL DETECTED" canvas with a producer-readable explanation of what ASA does, with honest pacing copy (4–5 min Phase 2 wait, not 2–5 min). * Patches group structure. Mirrors Mix Chain's emoji-eyebrow grouping (Drums / Bass / Synth / Master) so producers can jump to the bass patch without scanning 8 cards. * Input Source collapse (N9). Post-analysis, the Input Source panel collapses to a compact summary with filename + duration + "Analyze new file" + "Adjust settings". Frees the top of the page for the results the user came for. * AnalysisStatusPanel primary readout (#6). Stage diagnostic message promoted from 9px footnote to the visual focus of the progress card. Pre-existing tone-aware fill (running / success / failed) preserved. * Phase 2 failure mode (N1). Header subtitle derives from interpretation stage status (no more "PHASE COMPLETE" while INTERPRET still RUNNING/FAILED). StickyNav Phase 2 pills render disabled with hover-reason when sections didn't populate. Retry button gated on error.retryable; non-retryable failures surface the error code inline. * Misc audit follow-ups: BPM reconciled across exec card + Core Metrics tile (N2); Signal Monitor STANDBY canvas hidden when audio isn't playing, freeing ~160px (N7); StickyNav label "Device Chain" → "Sections" (N10, less ambiguous with Ableton's own effects-routing meaning); BASS group icon swapped from 🫧 → Lucide AudioWaveform (#13); toggle helper paragraphs switched from all-caps mono walls to sans-serif sentence case (#4 revised). * Phase 2 grammar post-process (audit final round). The prompt instruction added earlier didn't take — Gemini still emits "by recreates / by absorbs / by shapes" 3rd-person singular forms after "by" in role/reason text. Server-side _apply_phase2_grammar_fixes rewrites these to gerunds in-place on mixAndMasterChain[].reason, abletonRecommendations[].{reason,advancedTip}, and secretSauce.workflowSteps[].{instruction,measurementJustification}. Conservative regex (\bby \w{4,}s\b) + denylist guards against plural-noun false positives. Test coverage: * UI: 46 test files / 540 tests pass (was 39/422 before the audit). New service tests: userLabels (21), phase1Picker (25), citationBlock (12), appliedRecommendations (16), formatTrackDuration (12), interpretationSubtitle (10), workflowStagePrettifier (8), analysisStatusProgress (6), idleValuePropPanel + phase2NavReason. New DOM tests in analysisResultsUi.test.ts cover Track Layout citation, applied-checkbox flow, mix- chain citation rendering. * Backend: 16 new unit tests in test_phase2_grammar_fix.py cover _to_gerund, _fix_by_gerund_in_text, _apply_phase2_grammar_fixes including the actual screenshot corpus (recreates → recreating, shapes → shaping, matches → matching, etc.). Full suite: 463 of 463 ASA tests pass; 1 unrelated pre-existing failure in tests.test_url_ingest predates this branch. Visual verification: Playwright capture pass against a real 126s track confirmed every surface (15 screenshots in /tmp/asa-shots-audit-final/). Phase 2 returned in 220s; localStorage round-trip verified on applied-checkbox toggles. Documented limitations: * The gerund rule is algorithmic — verbs requiring consonant doubling (control → controlling, submit → submitting) degrade to "controling" / "submiting". Still better than "by controls". Drop a hand-mapped exception into the module if observed in real output. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix smoke tests for retired/renamed surfaces CI surfaced 7 smoke test failures from this branch's audit changes that hadn't been propagated to the smoke spec assertions: * `tests/smoke/ui-details.spec.ts` × 3 - `NO SIGNAL DETECTED` → `IdleValuePropPanel` (audit #5) - `JSON_DATA` / `REPORT_MD` button labels → `Download data` / `Download report` * `tests/smoke/responsive-layout.spec.ts` × 4 - `NO SIGNAL DETECTED` (×2) → `IdleValuePropPanel` - `CPU` text-presence checks removed; the two viewport-shape tests now assert just the model-selector responsive behavior (audit #11 retired the CPU meter; there's no element to assert) * `tests/smoke/file-validation.spec.ts` × 1 - `re-upload after results resets to file-selected state`: the test used `Remove File` (FileUpload component's affordance) to clear after results were visible. Post-N9 collapse, the Input Source panel replaces FileUpload with a compact summary card whose "↺ Analyze new file" button calls the same handleFileClear. Switched the test to target the new affordance. Also updated the e2e exports spec for label consistency (not in the failing CI job, but the same renames apply): * `tests/e2e/phase1-exports.spec.ts` - `downloadTextArtifact(page, /JSON_DATA/i)` → `/Download data/i` - `downloadTextArtifact(page, /REPORT_MD/i)` → `/Download report/i` Verified locally against the live stack: 45 of 46 smoke tests pass, 1 skipped (was unrelated). The previously-failing 7 are all green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * audit: nightly Phase-1 invariant + test guardrail (2026-05-14) 3 unit tests failing — all fixture-dependent live decision-gate comparators that assert instead of skipping when Gemini snapshots are absent. No Phase-boundary violations found. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: address review findings — decision-gate skip, gerund exceptions, dead branch, store prune - Skip decision_gate.{multi,real,stems}.live.test.ts when no /tmp snapshots are present (matches the existing decision_gate.live.test.ts pattern). Unblocks npm test on CI. - _to_gerund: add _GERUND_IRREGULARS map for consonant-doubling verbs (controls→controlling, submits→submitting, runs→running, etc.) — English doubling is stress-conditional, not worth implementing algorithmically. - _fix_grammar_in_record: drop the always-true conditional return and the unused `updated` flag. - appliedRecommendations: bound the localStorage store to MAX_TRACKED_FILES=50 (least-recently-updated wins eviction) so it can't grow without bound. * fix: formatTrackDuration carries seconds boundary; drop dead IdleSignalMonitor - formatTrackDuration: round seconds to total first, then derive mins/secs. Previously Math.round(seconds % 60) could yield 60, producing "0:60" for inputs like 59.5. New tests cover 59.5, 59.9, 119.5, 3599.7. - Delete IdleSignalMonitor.tsx (replaced by IdleValuePropPanel; not imported anywhere). Strip the "kept for future use" comments per CLAUDE.md. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
6 tasks
slittycode
added a commit
that referenced
this pull request
May 17, 2026
…esh (#57), nightly audit doc (#59) * audits: nightly 2026-05-16 — 5 issues (4 test failures + 1 review item) 3 vitest decision_gate.*.live.test.ts failures (missing /tmp snapshot gating), 1 Playwright upload-estimate-phase1.spec.ts timeout, and one mixDoctor.estimatePlr fallback flagged for human boundary review. No Phase 1 ground-truth mutations found. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(claude): add repo-layout, scripts map, Phase 3 UI entry, CSV recipe Closes four orientation gaps in CLAUDE.md: a top-of-Architecture map that flags advisory/, experiments/, docs/, and tests/ground_truth/ as off-path; a Scripts-at-a-glance section covering scripts/ and apps/backend/scripts/; a pointer from the Phase 3 paragraph to SamplePlayback.tsx + sampleGenerationClient.ts; and a curl example plus allowlisted field paths on the csv_export.py module entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add Layer 2 transcription evaluation guide Co-Authored-By: Oz <oz-agent@warp.dev> * fix(ui): stop Phase 2 results surface from leaking engine output Removes four classes of broken/placeholder content that made the Phase 2 recommendations read as engine output instead of producer-facing advice (audit finding #1). 1. Mix Chain card role text — `buildRoleSentence` used to fabricate "{stage phrase} by {verb}" by lowercasing the first letter of Gemini's `reason`. Because `reason` is a present-tense clause, every card produced ungrammatical splices like "Controls bass energy by ensures the extreme low-end mono…". Now renders the reason verbatim with a capitalized first letter and trailing period; HIGH-END cue suffix preserved as "(for …)". 2. Patch Framework `patchRole` — was a 7-key category-keyed fallback (`mapPatchRole`) that stamped duplicated placeholders ("Primary tone generator" on every SYNTHESIS card). Removed the field, the fallback, the JSX paragraph, and the contribution to the `inferProcessingGroup` text-concat. The category chip + per-card `whyThisWorks` already carry the bucket and explanation. 3. Patches/Mix Chain overlap — `buildPatchCards` now case-insensitively filters out devices that also appear in `mixAndMasterChain` so the Patches section stops re-listing chain devices. Synthetic fallbacks (Stereo Width, MIDI Clip Guide) are built from Phase 1 only and bypass the filter intentionally. 4. Interpretation Caution raw-JSON dump — the panel rendered `originalValue` verbatim, which for dropped recommendations is a JSON-dumped `AbletonRecommendation` (see `_stringify_warning_value` in `server_phase2.py`). Added `formatDroppedValue` helper that parses JSON-shaped values and renders a compact "device: X · parameter: Y · value: Z" summary. Non-JSON strings pass through; invalid JSON falls back to a truncated raw string. Also resolves the `$Saturator` symptom — Gemini's hallucinated `$`-prefixed device name now surfaces as a readable summary line instead of leaking through a raw JSON dump. 5. System Diagnostics dev-only leak — `validateNewFieldCoverage` emits "Phase 1 field 'X' is present… this warning is benign" coverage signals meant for the engine team, not producers. Added optional `audience?: 'dev' | 'user'` to `ValidationViolation`, marked `NEW_FIELD_UNCITED` as dev, and updated `Phase2ConsistencyReport` to filter dev-audience violations from the rendered table AND from the header counts (so the header doesn't read "5 warnings shown" above an empty table). The underlying `ValidationReport` still carries every violation for tests and offline analysis. Tripwire note: the backend grammar fix `_apply_phase2_grammar_fixes` in `server_phase2.py` only runs on the legacy `/api/phase2` endpoint, not the analysis-runs path the UI uses. After this change it becomes a redundant no-op against the new render shape; left untouched. Tests: 16 new unit cases across viewModel, UI rendering, validator, and consistency report. `npm run verify` passes (lint + 565 unit tests + build + 44 smoke tests). Live UI verified against saved run 855f1376… — all nine text-leak probes (`by ensures`, `by ducks`, `by generates`, `Primary tone generator`, `Texture and movement stage`, `Stereo placement stage`, raw `\$Saturator`, `this warning is benign`, "is present in the measurement payload but no Phase 2…") return false. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): surface primary citation in collapsed card headers Audit Finding #3 — "the chain-of-custody citation, the product's whole differentiator, renders as a footnote." Adds a one-line `CitationHeadline` primitive inside the collapsed header of every Mix Chain / Patch / Sonic Element card so producers see the measurement evidence at scan-time without expanding. The expanded CitationBlock stays in the body unchanged — this is the collapsed-state companion, not a replacement. Each headline reads `{label} {value} →` with the arrow pointing into the device h4/h3 that follows in the title row, making the implicit "measurement justified device" statement visible — exactly the audit's literal example "Crest factor 8.2 dB → Glue Compressor." Confidence sibling pills (Solid / Workable / Rough / Unreliable, same ladder as ConfidenceBandBadge) ride alongside the value when the primary field has a paired *Confidence sibling. This preserves the chain-of-custody invariant: low-confidence measurements visibly hedge in the collapsed view too, not just after expansion. Implementation: 1. `CitationHeadline` added as a sibling export in `apps/ui/src/components/CitationBlock.tsx`. Shares the file's imports, the `CONFIDENCE_PILL_CLASSES` map, and the audit lineage. Composes existing pure helpers (`pickPhase1Value`, `formatCitedValue`, `humanizeFieldPath`, `pickPhase1Confidence`, `getConfidenceBand`, `formatBandPillLabel`) — no new resolver logic. 2. Mount points in `AnalysisResults.tsx`: - Mix Chain card header (~line 2056): between the title row and the role paragraph. - Patch card header (~line 2215): between the title row and the MetaBadgeList. - Sonic Element card header (~line 1912): between the title row and the summary paragraph. 3. Each mount is guarded by `card.phase1Fields.length > 0`. Cards without cited fields fall back to today's exact layout. Live verification against saved run 855f1376… (the audit reproducer): all 10 Mix Chain cards, 2 Patch cards, and 7 Sonic Element cards render the headline correctly with appropriate confidence pills (PUMPING STRENGTH 47% [ROUGH SKETCH 30%], SUPERSAW DETECTED [WORKABLE DRAFT 78%], ACID BASS DETECTED [SOLID SCAFFOLD 99%], NOTE TRANSCRIPTION 61% [WORKABLE DRAFT 61%], KEY C Minor [SOLID SCAFFOLD 93%], TEMPO 145 BPM [SOLID SCAFFOLD 100%]). Mobile (375px) verified — headlines fit, pills stay aligned, no overflow. Tests: 15 new cases — 10 in `tests/services/citationHeadline.test.ts` (mirrors `citationBlock.test.ts`) covering label/value/arrow render, null fallback, confidence-pill bands, value formatter parity, typography hooks. 5 in `analysisResultsUi.test.ts` covering Mix Chain / Patch / Sonic integration, empty-fields fallback, and the low-confidence Unreliable-band path. `npm run verify` passes: lint + 575 unit tests + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): unify five confidence vocabularies onto the canonical band ladder Audit Finding #4 — the producer used to see five competing vocabularies for "how much should I trust this number?" — HIGH/MED/LOW pills on Detected Characteristics cards, High/Moderate/Low chips on Confidence Notes, bare CONF X% text on Key/Character metric cards, SCORE X.XX badges on the BPM card (two render paths), and the canonical four-band ladder used only by the Session Musician panel. The chain-of-custody invariant in PURPOSE.md requires low-confidence measurements to produce visibly hedged advice — five vocabularies fragment that signal so the producer can't build a single mental model of trust. This PR promotes the existing four-band ladder (apps/ui/src/services/sessionMusician/confidenceBand.ts) to the canonical primitive across every confidence surface. Frontend-only; Gemini still emits HIGH/MED/LOW and the UI converts locally via a new `toConfidenceBand` normalizer. What changed: 1. New normalizer `toConfidenceBand(value)` in `confidenceBand.ts` accepts numeric 0-1 floats, 0-100 integers, Gemini string enums (HIGH/MED/LOW + High/Moderate/Low variants), and percent strings ("62%"). Returns the matching `ConfidenceBand` or null for unparseable input. The string enums map to band-mid values (HIGH→0.9, MED→0.6, LOW→0.3) so `formatBandPillLabel` reads as honest hedges. 2. `ConfidenceBandBadge` gains: - `variant: 'full' | 'compact'` — `compact` omits the copy paragraph so the pill can sit inline in card corners and metric-card footers. `full` (default) preserves the Session Musician panel behavior unchanged. - `band?: ConfidenceBand` override prop — when supplied, skips the `getConfidenceBand` round-trip. Useful when the caller pre-converts a string enum. 3. V1: Detected Characteristics cards (AnalysisResults.tsx ~1650) — bespoke HIGH/MED/LOW ternary pill replaced with `<ConfidenceBandBadge variant="compact" band={toConfidenceBand(item.confidence)} />`. `characteristicPillClass` helper kept alive only for the unrelated characteristic-name chips on the Character metric card (line 1000); flagged as a follow-up. 4. V2: Confidence Notes chips (AnalysisResults.tsx ~1187) — `toConfidenceBadges` viewModel return shape changed from `{ label, level }` (3-level legacy enum) to `{ label, band }` (canonical four-band ladder). `confidenceClass` helper deleted. 5. V3: Plain `CONF X%` text (3 sites) — replaced with `<ConfidenceBandBadge variant="compact" confidence={...} />` in: - AnalysisResults.tsx Key card footer - AnalysisResults.tsx Character card footer - MeasurementDashboard.tsx alternate Key card 6. V4: `SCORE X.XX` badges (2 sites) — replaced with band pills in AnalysisResults.tsx Tempo card and MeasurementDashboard.tsx Tempo card. `formatBpmScore` deleted from both files. Cross-Check ✓/✗ StatusBadge in MeasurementDashboard preserved — that's an agreement signal (do multiple BPM detectors agree?), not confidence. 7. Dead code retired: `normalizeConfidenceLevel`, `parseConfidenceScalar`, `confidenceClass`, two copies of `formatBpmScore`. `ConfidenceLevel` type and `MelodyInsightsViewModel.confidenceLabel` kept alive — they drive non-pill key/value text rows in Sonic Element melody insights (flagged for follow-up migration). 3-level → 4-band mismatch documented as an intentional refinement: the old `normalizeConfidenceLevel` mapped scalar 0.5-0.79 AND "medium" both to "Moderate"; the new normalizer maps "medium" to 0.6 → workable (consistent), and scalar 0.25-0.49 to "Rough sketch" (more granular than the old "Low" bucket). Tests: 20 new cases across 4 files — 1. `confidenceBand.test.ts` — 9 cases for `toConfidenceBand` (null/undefined fallback, 0-1 floats, 0-100 integers, all string enum variants, percent strings, NaN/Infinity, negative clamping). 2. `confidenceBandBadge.test.ts` — 4 cases for the compact variant and the `band` override prop (label-only when no confidence, tone override, Gemini-string routing). 3. `analysisResultsViewModel.test.ts` — rewrote the existing `toConfidenceBadges` shape assertion; added a `band: null` fallback case. 4. `analysisResultsUi.test.ts` — 5 integration cases proving each V1-V4 site renders band pill text and not the old vocabularies. Live UI verified against saved run 855f1376… — 36 band pills visible across the page (21 Solid scaffold, 7 Workable draft, 5 Rough sketch, 3 Unreliable). Zero `CONF X%` leakage. Detected Characteristics cards show the chain-of-custody hedge working as designed (Detuned Supersaw renders WORKABLE DRAFT in orange while the other 4 detections render SOLID SCAFFOLD in green). Cross-Check ✓/✗ structurally preserved (the saved run doesn't populate bpmAgreement, so it doesn't render, but the code path is untouched). `npm run verify` passes: lint + ~600 unit + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs: refresh stale items against current architecture CHANGELOG.md "Unreleased" was missing nine shipped features (Phase 3 audition samples, URL ingest, admin DELETE bypass, source-audio route, CSV export, publicStatus, reassigned spectrogram, chain-of-custody audit overhaul, BatchedBandpass centralization, hosted runtime foundation, backend monolith split). Added them. CLAUDE.md's analyze.py CLI example only listed four flags; expanded to match the full surface (--standard, --pitch-note-only, --stem-dir, --stem-output-dir, --pitch-note-backend). Expanded the Frontend key service files list from 9 to 16 entries to cover the services that landed during the audit overhaul (httpClient, sampleGenerationClient, appliedRecommendations + userLabels, phase1Picker + phaseLabels, audioFile, fieldAnalytics + diagnosticLogs, sessionMusician helpers). apps/ui/AGENTS.md File Map likewise expanded from 4 to 14 service entries against the same list. docs/SAMPLE_GENERATION.md's "Snapshot integration" section claimed an `AnalysisRunSnapshot.stages.sampleGeneration` shape that does not exist in apps/ui/src/types/backend.ts — the snapshot tracks only measurement, pitchNoteTranslation, and interpretation. Replaced the stale TS block with a description that matches the actual on-demand artifact path (consistent with the doc's own line 47). apps/backend/AGENTS.md and apps/backend/ARCHITECTURE.md now flag symbolic_extract.py as orphaned and broken — it imports a removed BasicPitchBackend symbol from analyze.py, would raise ImportError at load, and is not referenced from any other module. Slated for removal. docs/history/README.md had `library-review-torchfx-2026-05-13.md` listed twice; removed the duplicate. docs/ARCHITECTURE_STRATEGY.md "Last updated" stamp bumped to May 2026 with a note that no shifts to the three-layer thesis or library decisions accompanied the recent feature work. Date stamps in apps/backend/AGENTS.md and apps/ui/AGENTS.md refreshed to 2026-05-17. https://claude.ai/code/session_01QEmFAUJSoRgTqPcFTfcnYW * fix(ui): audit quick-hits bundle (StickyNav, Mix Chain, Mix Doctor, header) Closes six items from the audit's "Quick hits (low ROI, real)" list. Each is independent; bundled into one PR because they're all small surgical fixes. 1. QH1 — StickyNav clipped "MEASUREMENTS" at 1440px. Was overflow-x-auto + min-w-max, which produced an invisible horizontal scroll (macOS hides scrollbars by default; last pill looked clipped with no scroll affordance). Switched to flex-wrap so pills flow onto multiple rows at narrow widths; whitespace-nowrap prevents intra- label wrap. 2. QH2 — Mix Chain card-number order badges removed. Cards are grouped by processing stage AFTER ordering, so the order numbers appeared out-of-sequence within each group ("1, 6, 8, 9 / 2, 4 / 5, 7 / 3 / 10"), which read as a presentation bug. The visual sequence within each group is already meaningful; the badge added confusion without information. Dropped. 3. QH4 — Mix Doctor Band Diagnostics table now shows the target range alongside the optimal. Previously the column rendered only the optimal dB (e.g. "-22.0"), so two bands with similar Delta dB could land in different Issue buckets without the producer being able to see why. The Issue is determined by absolute thresholds (target.minDb / maxDb), not by the diff from optimal. Showing the range makes the verdict legible at a glance — Sub Bass -6.4 norm, range -16 to -8, exceeds the upper bound by 1.6 dB → TOO-LOUD; Low Mids -14.5 norm, range -26 to -14, sits at the boundary → OPTIMAL. Threshold logic itself was correct; this is a display fix. 4. QH5 — "Dense DAW Lab" link removed from the header. A prior audit pass had de-emphasized it to muted text, but the new audit re- flagged it for sitting in the primary flow's header without context for what it is. Route stays accessible via direct URL (the getAppViewHref helper is preserved for future re-introduction behind a settings menu, and main.tsx still routes to the DenseDawConcept component when activeView === 'daw-concept'). 5. QH6 — header model selector visually demoted. Dropped the "Interpretation Model" label and shrank the styling from a bordered dropdown to a discreet text-secondary inline select. The audit flagged the prior styling as foregrounding an AI-model choice an intermediate producer has no basis to make. Selector stays accessible (smoke spec requires it visible at desktop viewport; `responsive-layout.spec.ts` and `upload-estimate-phase1.spec.ts` both guard the `phase2-model-desktop` testid) but now reads as background metadata. Aria-label + title attribute carry the long- form context for assistive tech and hover discoverability. 6. QH7 — Mix Doctor section title renamed from "MixDoctor" to "Mix Doctor". The audit flagged that the section header rendered as "Mixdoctor" with a lowercase 'd', inconsistent with every other section name. Root cause: `formatWord` in `utils/displayText.ts` case-normalizes single-token CamelCase to sentence case, so "MixDoctor" was rendering as "Mixdoctor". Two words ("Mix Doctor") matches every other section name's pattern (Style Profile, Project Setup, Track Layout, Mix & Master Chain, Patch Framework, etc.) and renders cleanly through the existing pipeline. `npm run verify` passes: lint + ~600 unit + build + 44 smoke. Live UI verified against saved run 855f1376… with probes confirming all six fixes: - StickyNav.overflow-x-auto wrapper gone (pills wrap to 2 rows) - Zero Mix Chain order badges in #section-mix-chain - "Target (range)" header present, "Target dB" header gone - "Dense DAW Lab" text absent from page - "Interpretation Model" label absent from page - "Mix Doctor" section title present, "Mixdoctor" absent Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Oz <oz-agent@warp.dev>
slittycode
added a commit
that referenced
this pull request
May 17, 2026
…ladder (#56) * docs(claude): add repo-layout, scripts map, Phase 3 UI entry, CSV recipe Closes four orientation gaps in CLAUDE.md: a top-of-Architecture map that flags advisory/, experiments/, docs/, and tests/ground_truth/ as off-path; a Scripts-at-a-glance section covering scripts/ and apps/backend/scripts/; a pointer from the Phase 3 paragraph to SamplePlayback.tsx + sampleGenerationClient.ts; and a curl example plus allowlisted field paths on the csv_export.py module entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add Layer 2 transcription evaluation guide Co-Authored-By: Oz <oz-agent@warp.dev> * fix(ui): stop Phase 2 results surface from leaking engine output Removes four classes of broken/placeholder content that made the Phase 2 recommendations read as engine output instead of producer-facing advice (audit finding #1). 1. Mix Chain card role text — `buildRoleSentence` used to fabricate "{stage phrase} by {verb}" by lowercasing the first letter of Gemini's `reason`. Because `reason` is a present-tense clause, every card produced ungrammatical splices like "Controls bass energy by ensures the extreme low-end mono…". Now renders the reason verbatim with a capitalized first letter and trailing period; HIGH-END cue suffix preserved as "(for …)". 2. Patch Framework `patchRole` — was a 7-key category-keyed fallback (`mapPatchRole`) that stamped duplicated placeholders ("Primary tone generator" on every SYNTHESIS card). Removed the field, the fallback, the JSX paragraph, and the contribution to the `inferProcessingGroup` text-concat. The category chip + per-card `whyThisWorks` already carry the bucket and explanation. 3. Patches/Mix Chain overlap — `buildPatchCards` now case-insensitively filters out devices that also appear in `mixAndMasterChain` so the Patches section stops re-listing chain devices. Synthetic fallbacks (Stereo Width, MIDI Clip Guide) are built from Phase 1 only and bypass the filter intentionally. 4. Interpretation Caution raw-JSON dump — the panel rendered `originalValue` verbatim, which for dropped recommendations is a JSON-dumped `AbletonRecommendation` (see `_stringify_warning_value` in `server_phase2.py`). Added `formatDroppedValue` helper that parses JSON-shaped values and renders a compact "device: X · parameter: Y · value: Z" summary. Non-JSON strings pass through; invalid JSON falls back to a truncated raw string. Also resolves the `$Saturator` symptom — Gemini's hallucinated `$`-prefixed device name now surfaces as a readable summary line instead of leaking through a raw JSON dump. 5. System Diagnostics dev-only leak — `validateNewFieldCoverage` emits "Phase 1 field 'X' is present… this warning is benign" coverage signals meant for the engine team, not producers. Added optional `audience?: 'dev' | 'user'` to `ValidationViolation`, marked `NEW_FIELD_UNCITED` as dev, and updated `Phase2ConsistencyReport` to filter dev-audience violations from the rendered table AND from the header counts (so the header doesn't read "5 warnings shown" above an empty table). The underlying `ValidationReport` still carries every violation for tests and offline analysis. Tripwire note: the backend grammar fix `_apply_phase2_grammar_fixes` in `server_phase2.py` only runs on the legacy `/api/phase2` endpoint, not the analysis-runs path the UI uses. After this change it becomes a redundant no-op against the new render shape; left untouched. Tests: 16 new unit cases across viewModel, UI rendering, validator, and consistency report. `npm run verify` passes (lint + 565 unit tests + build + 44 smoke tests). Live UI verified against saved run 855f1376… — all nine text-leak probes (`by ensures`, `by ducks`, `by generates`, `Primary tone generator`, `Texture and movement stage`, `Stereo placement stage`, raw `\$Saturator`, `this warning is benign`, "is present in the measurement payload but no Phase 2…") return false. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): surface primary citation in collapsed card headers Audit Finding #3 — "the chain-of-custody citation, the product's whole differentiator, renders as a footnote." Adds a one-line `CitationHeadline` primitive inside the collapsed header of every Mix Chain / Patch / Sonic Element card so producers see the measurement evidence at scan-time without expanding. The expanded CitationBlock stays in the body unchanged — this is the collapsed-state companion, not a replacement. Each headline reads `{label} {value} →` with the arrow pointing into the device h4/h3 that follows in the title row, making the implicit "measurement justified device" statement visible — exactly the audit's literal example "Crest factor 8.2 dB → Glue Compressor." Confidence sibling pills (Solid / Workable / Rough / Unreliable, same ladder as ConfidenceBandBadge) ride alongside the value when the primary field has a paired *Confidence sibling. This preserves the chain-of-custody invariant: low-confidence measurements visibly hedge in the collapsed view too, not just after expansion. Implementation: 1. `CitationHeadline` added as a sibling export in `apps/ui/src/components/CitationBlock.tsx`. Shares the file's imports, the `CONFIDENCE_PILL_CLASSES` map, and the audit lineage. Composes existing pure helpers (`pickPhase1Value`, `formatCitedValue`, `humanizeFieldPath`, `pickPhase1Confidence`, `getConfidenceBand`, `formatBandPillLabel`) — no new resolver logic. 2. Mount points in `AnalysisResults.tsx`: - Mix Chain card header (~line 2056): between the title row and the role paragraph. - Patch card header (~line 2215): between the title row and the MetaBadgeList. - Sonic Element card header (~line 1912): between the title row and the summary paragraph. 3. Each mount is guarded by `card.phase1Fields.length > 0`. Cards without cited fields fall back to today's exact layout. Live verification against saved run 855f1376… (the audit reproducer): all 10 Mix Chain cards, 2 Patch cards, and 7 Sonic Element cards render the headline correctly with appropriate confidence pills (PUMPING STRENGTH 47% [ROUGH SKETCH 30%], SUPERSAW DETECTED [WORKABLE DRAFT 78%], ACID BASS DETECTED [SOLID SCAFFOLD 99%], NOTE TRANSCRIPTION 61% [WORKABLE DRAFT 61%], KEY C Minor [SOLID SCAFFOLD 93%], TEMPO 145 BPM [SOLID SCAFFOLD 100%]). Mobile (375px) verified — headlines fit, pills stay aligned, no overflow. Tests: 15 new cases — 10 in `tests/services/citationHeadline.test.ts` (mirrors `citationBlock.test.ts`) covering label/value/arrow render, null fallback, confidence-pill bands, value formatter parity, typography hooks. 5 in `analysisResultsUi.test.ts` covering Mix Chain / Patch / Sonic integration, empty-fields fallback, and the low-confidence Unreliable-band path. `npm run verify` passes: lint + 575 unit tests + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): unify five confidence vocabularies onto the canonical band ladder Audit Finding #4 — the producer used to see five competing vocabularies for "how much should I trust this number?" — HIGH/MED/LOW pills on Detected Characteristics cards, High/Moderate/Low chips on Confidence Notes, bare CONF X% text on Key/Character metric cards, SCORE X.XX badges on the BPM card (two render paths), and the canonical four-band ladder used only by the Session Musician panel. The chain-of-custody invariant in PURPOSE.md requires low-confidence measurements to produce visibly hedged advice — five vocabularies fragment that signal so the producer can't build a single mental model of trust. This PR promotes the existing four-band ladder (apps/ui/src/services/sessionMusician/confidenceBand.ts) to the canonical primitive across every confidence surface. Frontend-only; Gemini still emits HIGH/MED/LOW and the UI converts locally via a new `toConfidenceBand` normalizer. What changed: 1. New normalizer `toConfidenceBand(value)` in `confidenceBand.ts` accepts numeric 0-1 floats, 0-100 integers, Gemini string enums (HIGH/MED/LOW + High/Moderate/Low variants), and percent strings ("62%"). Returns the matching `ConfidenceBand` or null for unparseable input. The string enums map to band-mid values (HIGH→0.9, MED→0.6, LOW→0.3) so `formatBandPillLabel` reads as honest hedges. 2. `ConfidenceBandBadge` gains: - `variant: 'full' | 'compact'` — `compact` omits the copy paragraph so the pill can sit inline in card corners and metric-card footers. `full` (default) preserves the Session Musician panel behavior unchanged. - `band?: ConfidenceBand` override prop — when supplied, skips the `getConfidenceBand` round-trip. Useful when the caller pre-converts a string enum. 3. V1: Detected Characteristics cards (AnalysisResults.tsx ~1650) — bespoke HIGH/MED/LOW ternary pill replaced with `<ConfidenceBandBadge variant="compact" band={toConfidenceBand(item.confidence)} />`. `characteristicPillClass` helper kept alive only for the unrelated characteristic-name chips on the Character metric card (line 1000); flagged as a follow-up. 4. V2: Confidence Notes chips (AnalysisResults.tsx ~1187) — `toConfidenceBadges` viewModel return shape changed from `{ label, level }` (3-level legacy enum) to `{ label, band }` (canonical four-band ladder). `confidenceClass` helper deleted. 5. V3: Plain `CONF X%` text (3 sites) — replaced with `<ConfidenceBandBadge variant="compact" confidence={...} />` in: - AnalysisResults.tsx Key card footer - AnalysisResults.tsx Character card footer - MeasurementDashboard.tsx alternate Key card 6. V4: `SCORE X.XX` badges (2 sites) — replaced with band pills in AnalysisResults.tsx Tempo card and MeasurementDashboard.tsx Tempo card. `formatBpmScore` deleted from both files. Cross-Check ✓/✗ StatusBadge in MeasurementDashboard preserved — that's an agreement signal (do multiple BPM detectors agree?), not confidence. 7. Dead code retired: `normalizeConfidenceLevel`, `parseConfidenceScalar`, `confidenceClass`, two copies of `formatBpmScore`. `ConfidenceLevel` type and `MelodyInsightsViewModel.confidenceLabel` kept alive — they drive non-pill key/value text rows in Sonic Element melody insights (flagged for follow-up migration). 3-level → 4-band mismatch documented as an intentional refinement: the old `normalizeConfidenceLevel` mapped scalar 0.5-0.79 AND "medium" both to "Moderate"; the new normalizer maps "medium" to 0.6 → workable (consistent), and scalar 0.25-0.49 to "Rough sketch" (more granular than the old "Low" bucket). Tests: 20 new cases across 4 files — 1. `confidenceBand.test.ts` — 9 cases for `toConfidenceBand` (null/undefined fallback, 0-1 floats, 0-100 integers, all string enum variants, percent strings, NaN/Infinity, negative clamping). 2. `confidenceBandBadge.test.ts` — 4 cases for the compact variant and the `band` override prop (label-only when no confidence, tone override, Gemini-string routing). 3. `analysisResultsViewModel.test.ts` — rewrote the existing `toConfidenceBadges` shape assertion; added a `band: null` fallback case. 4. `analysisResultsUi.test.ts` — 5 integration cases proving each V1-V4 site renders band pill text and not the old vocabularies. Live UI verified against saved run 855f1376… — 36 band pills visible across the page (21 Solid scaffold, 7 Workable draft, 5 Rough sketch, 3 Unreliable). Zero `CONF X%` leakage. Detected Characteristics cards show the chain-of-custody hedge working as designed (Detuned Supersaw renders WORKABLE DRAFT in orange while the other 4 detections render SOLID SCAFFOLD in green). Cross-Check ✓/✗ structurally preserved (the saved run doesn't populate bpmAgreement, so it doesn't render, but the code path is untouched). `npm run verify` passes: lint + ~600 unit + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Oz <oz-agent@warp.dev>
slittycode
added a commit
that referenced
this pull request
May 17, 2026
…eader) (#58) * docs(claude): add repo-layout, scripts map, Phase 3 UI entry, CSV recipe Closes four orientation gaps in CLAUDE.md: a top-of-Architecture map that flags advisory/, experiments/, docs/, and tests/ground_truth/ as off-path; a Scripts-at-a-glance section covering scripts/ and apps/backend/scripts/; a pointer from the Phase 3 paragraph to SamplePlayback.tsx + sampleGenerationClient.ts; and a curl example plus allowlisted field paths on the csv_export.py module entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add Layer 2 transcription evaluation guide Co-Authored-By: Oz <oz-agent@warp.dev> * fix(ui): stop Phase 2 results surface from leaking engine output Removes four classes of broken/placeholder content that made the Phase 2 recommendations read as engine output instead of producer-facing advice (audit finding #1). 1. Mix Chain card role text — `buildRoleSentence` used to fabricate "{stage phrase} by {verb}" by lowercasing the first letter of Gemini's `reason`. Because `reason` is a present-tense clause, every card produced ungrammatical splices like "Controls bass energy by ensures the extreme low-end mono…". Now renders the reason verbatim with a capitalized first letter and trailing period; HIGH-END cue suffix preserved as "(for …)". 2. Patch Framework `patchRole` — was a 7-key category-keyed fallback (`mapPatchRole`) that stamped duplicated placeholders ("Primary tone generator" on every SYNTHESIS card). Removed the field, the fallback, the JSX paragraph, and the contribution to the `inferProcessingGroup` text-concat. The category chip + per-card `whyThisWorks` already carry the bucket and explanation. 3. Patches/Mix Chain overlap — `buildPatchCards` now case-insensitively filters out devices that also appear in `mixAndMasterChain` so the Patches section stops re-listing chain devices. Synthetic fallbacks (Stereo Width, MIDI Clip Guide) are built from Phase 1 only and bypass the filter intentionally. 4. Interpretation Caution raw-JSON dump — the panel rendered `originalValue` verbatim, which for dropped recommendations is a JSON-dumped `AbletonRecommendation` (see `_stringify_warning_value` in `server_phase2.py`). Added `formatDroppedValue` helper that parses JSON-shaped values and renders a compact "device: X · parameter: Y · value: Z" summary. Non-JSON strings pass through; invalid JSON falls back to a truncated raw string. Also resolves the `$Saturator` symptom — Gemini's hallucinated `$`-prefixed device name now surfaces as a readable summary line instead of leaking through a raw JSON dump. 5. System Diagnostics dev-only leak — `validateNewFieldCoverage` emits "Phase 1 field 'X' is present… this warning is benign" coverage signals meant for the engine team, not producers. Added optional `audience?: 'dev' | 'user'` to `ValidationViolation`, marked `NEW_FIELD_UNCITED` as dev, and updated `Phase2ConsistencyReport` to filter dev-audience violations from the rendered table AND from the header counts (so the header doesn't read "5 warnings shown" above an empty table). The underlying `ValidationReport` still carries every violation for tests and offline analysis. Tripwire note: the backend grammar fix `_apply_phase2_grammar_fixes` in `server_phase2.py` only runs on the legacy `/api/phase2` endpoint, not the analysis-runs path the UI uses. After this change it becomes a redundant no-op against the new render shape; left untouched. Tests: 16 new unit cases across viewModel, UI rendering, validator, and consistency report. `npm run verify` passes (lint + 565 unit tests + build + 44 smoke tests). Live UI verified against saved run 855f1376… — all nine text-leak probes (`by ensures`, `by ducks`, `by generates`, `Primary tone generator`, `Texture and movement stage`, `Stereo placement stage`, raw `\$Saturator`, `this warning is benign`, "is present in the measurement payload but no Phase 2…") return false. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): surface primary citation in collapsed card headers Audit Finding #3 — "the chain-of-custody citation, the product's whole differentiator, renders as a footnote." Adds a one-line `CitationHeadline` primitive inside the collapsed header of every Mix Chain / Patch / Sonic Element card so producers see the measurement evidence at scan-time without expanding. The expanded CitationBlock stays in the body unchanged — this is the collapsed-state companion, not a replacement. Each headline reads `{label} {value} →` with the arrow pointing into the device h4/h3 that follows in the title row, making the implicit "measurement justified device" statement visible — exactly the audit's literal example "Crest factor 8.2 dB → Glue Compressor." Confidence sibling pills (Solid / Workable / Rough / Unreliable, same ladder as ConfidenceBandBadge) ride alongside the value when the primary field has a paired *Confidence sibling. This preserves the chain-of-custody invariant: low-confidence measurements visibly hedge in the collapsed view too, not just after expansion. Implementation: 1. `CitationHeadline` added as a sibling export in `apps/ui/src/components/CitationBlock.tsx`. Shares the file's imports, the `CONFIDENCE_PILL_CLASSES` map, and the audit lineage. Composes existing pure helpers (`pickPhase1Value`, `formatCitedValue`, `humanizeFieldPath`, `pickPhase1Confidence`, `getConfidenceBand`, `formatBandPillLabel`) — no new resolver logic. 2. Mount points in `AnalysisResults.tsx`: - Mix Chain card header (~line 2056): between the title row and the role paragraph. - Patch card header (~line 2215): between the title row and the MetaBadgeList. - Sonic Element card header (~line 1912): between the title row and the summary paragraph. 3. Each mount is guarded by `card.phase1Fields.length > 0`. Cards without cited fields fall back to today's exact layout. Live verification against saved run 855f1376… (the audit reproducer): all 10 Mix Chain cards, 2 Patch cards, and 7 Sonic Element cards render the headline correctly with appropriate confidence pills (PUMPING STRENGTH 47% [ROUGH SKETCH 30%], SUPERSAW DETECTED [WORKABLE DRAFT 78%], ACID BASS DETECTED [SOLID SCAFFOLD 99%], NOTE TRANSCRIPTION 61% [WORKABLE DRAFT 61%], KEY C Minor [SOLID SCAFFOLD 93%], TEMPO 145 BPM [SOLID SCAFFOLD 100%]). Mobile (375px) verified — headlines fit, pills stay aligned, no overflow. Tests: 15 new cases — 10 in `tests/services/citationHeadline.test.ts` (mirrors `citationBlock.test.ts`) covering label/value/arrow render, null fallback, confidence-pill bands, value formatter parity, typography hooks. 5 in `analysisResultsUi.test.ts` covering Mix Chain / Patch / Sonic integration, empty-fields fallback, and the low-confidence Unreliable-band path. `npm run verify` passes: lint + 575 unit tests + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ui): unify five confidence vocabularies onto the canonical band ladder Audit Finding #4 — the producer used to see five competing vocabularies for "how much should I trust this number?" — HIGH/MED/LOW pills on Detected Characteristics cards, High/Moderate/Low chips on Confidence Notes, bare CONF X% text on Key/Character metric cards, SCORE X.XX badges on the BPM card (two render paths), and the canonical four-band ladder used only by the Session Musician panel. The chain-of-custody invariant in PURPOSE.md requires low-confidence measurements to produce visibly hedged advice — five vocabularies fragment that signal so the producer can't build a single mental model of trust. This PR promotes the existing four-band ladder (apps/ui/src/services/sessionMusician/confidenceBand.ts) to the canonical primitive across every confidence surface. Frontend-only; Gemini still emits HIGH/MED/LOW and the UI converts locally via a new `toConfidenceBand` normalizer. What changed: 1. New normalizer `toConfidenceBand(value)` in `confidenceBand.ts` accepts numeric 0-1 floats, 0-100 integers, Gemini string enums (HIGH/MED/LOW + High/Moderate/Low variants), and percent strings ("62%"). Returns the matching `ConfidenceBand` or null for unparseable input. The string enums map to band-mid values (HIGH→0.9, MED→0.6, LOW→0.3) so `formatBandPillLabel` reads as honest hedges. 2. `ConfidenceBandBadge` gains: - `variant: 'full' | 'compact'` — `compact` omits the copy paragraph so the pill can sit inline in card corners and metric-card footers. `full` (default) preserves the Session Musician panel behavior unchanged. - `band?: ConfidenceBand` override prop — when supplied, skips the `getConfidenceBand` round-trip. Useful when the caller pre-converts a string enum. 3. V1: Detected Characteristics cards (AnalysisResults.tsx ~1650) — bespoke HIGH/MED/LOW ternary pill replaced with `<ConfidenceBandBadge variant="compact" band={toConfidenceBand(item.confidence)} />`. `characteristicPillClass` helper kept alive only for the unrelated characteristic-name chips on the Character metric card (line 1000); flagged as a follow-up. 4. V2: Confidence Notes chips (AnalysisResults.tsx ~1187) — `toConfidenceBadges` viewModel return shape changed from `{ label, level }` (3-level legacy enum) to `{ label, band }` (canonical four-band ladder). `confidenceClass` helper deleted. 5. V3: Plain `CONF X%` text (3 sites) — replaced with `<ConfidenceBandBadge variant="compact" confidence={...} />` in: - AnalysisResults.tsx Key card footer - AnalysisResults.tsx Character card footer - MeasurementDashboard.tsx alternate Key card 6. V4: `SCORE X.XX` badges (2 sites) — replaced with band pills in AnalysisResults.tsx Tempo card and MeasurementDashboard.tsx Tempo card. `formatBpmScore` deleted from both files. Cross-Check ✓/✗ StatusBadge in MeasurementDashboard preserved — that's an agreement signal (do multiple BPM detectors agree?), not confidence. 7. Dead code retired: `normalizeConfidenceLevel`, `parseConfidenceScalar`, `confidenceClass`, two copies of `formatBpmScore`. `ConfidenceLevel` type and `MelodyInsightsViewModel.confidenceLabel` kept alive — they drive non-pill key/value text rows in Sonic Element melody insights (flagged for follow-up migration). 3-level → 4-band mismatch documented as an intentional refinement: the old `normalizeConfidenceLevel` mapped scalar 0.5-0.79 AND "medium" both to "Moderate"; the new normalizer maps "medium" to 0.6 → workable (consistent), and scalar 0.25-0.49 to "Rough sketch" (more granular than the old "Low" bucket). Tests: 20 new cases across 4 files — 1. `confidenceBand.test.ts` — 9 cases for `toConfidenceBand` (null/undefined fallback, 0-1 floats, 0-100 integers, all string enum variants, percent strings, NaN/Infinity, negative clamping). 2. `confidenceBandBadge.test.ts` — 4 cases for the compact variant and the `band` override prop (label-only when no confidence, tone override, Gemini-string routing). 3. `analysisResultsViewModel.test.ts` — rewrote the existing `toConfidenceBadges` shape assertion; added a `band: null` fallback case. 4. `analysisResultsUi.test.ts` — 5 integration cases proving each V1-V4 site renders band pill text and not the old vocabularies. Live UI verified against saved run 855f1376… — 36 band pills visible across the page (21 Solid scaffold, 7 Workable draft, 5 Rough sketch, 3 Unreliable). Zero `CONF X%` leakage. Detected Characteristics cards show the chain-of-custody hedge working as designed (Detuned Supersaw renders WORKABLE DRAFT in orange while the other 4 detections render SOLID SCAFFOLD in green). Cross-Check ✓/✗ structurally preserved (the saved run doesn't populate bpmAgreement, so it doesn't render, but the code path is untouched). `npm run verify` passes: lint + ~600 unit + build + 44 smoke. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(ui): audit quick-hits bundle (StickyNav, Mix Chain, Mix Doctor, header) Closes six items from the audit's "Quick hits (low ROI, real)" list. Each is independent; bundled into one PR because they're all small surgical fixes. 1. QH1 — StickyNav clipped "MEASUREMENTS" at 1440px. Was overflow-x-auto + min-w-max, which produced an invisible horizontal scroll (macOS hides scrollbars by default; last pill looked clipped with no scroll affordance). Switched to flex-wrap so pills flow onto multiple rows at narrow widths; whitespace-nowrap prevents intra- label wrap. 2. QH2 — Mix Chain card-number order badges removed. Cards are grouped by processing stage AFTER ordering, so the order numbers appeared out-of-sequence within each group ("1, 6, 8, 9 / 2, 4 / 5, 7 / 3 / 10"), which read as a presentation bug. The visual sequence within each group is already meaningful; the badge added confusion without information. Dropped. 3. QH4 — Mix Doctor Band Diagnostics table now shows the target range alongside the optimal. Previously the column rendered only the optimal dB (e.g. "-22.0"), so two bands with similar Delta dB could land in different Issue buckets without the producer being able to see why. The Issue is determined by absolute thresholds (target.minDb / maxDb), not by the diff from optimal. Showing the range makes the verdict legible at a glance — Sub Bass -6.4 norm, range -16 to -8, exceeds the upper bound by 1.6 dB → TOO-LOUD; Low Mids -14.5 norm, range -26 to -14, sits at the boundary → OPTIMAL. Threshold logic itself was correct; this is a display fix. 4. QH5 — "Dense DAW Lab" link removed from the header. A prior audit pass had de-emphasized it to muted text, but the new audit re- flagged it for sitting in the primary flow's header without context for what it is. Route stays accessible via direct URL (the getAppViewHref helper is preserved for future re-introduction behind a settings menu, and main.tsx still routes to the DenseDawConcept component when activeView === 'daw-concept'). 5. QH6 — header model selector visually demoted. Dropped the "Interpretation Model" label and shrank the styling from a bordered dropdown to a discreet text-secondary inline select. The audit flagged the prior styling as foregrounding an AI-model choice an intermediate producer has no basis to make. Selector stays accessible (smoke spec requires it visible at desktop viewport; `responsive-layout.spec.ts` and `upload-estimate-phase1.spec.ts` both guard the `phase2-model-desktop` testid) but now reads as background metadata. Aria-label + title attribute carry the long- form context for assistive tech and hover discoverability. 6. QH7 — Mix Doctor section title renamed from "MixDoctor" to "Mix Doctor". The audit flagged that the section header rendered as "Mixdoctor" with a lowercase 'd', inconsistent with every other section name. Root cause: `formatWord` in `utils/displayText.ts` case-normalizes single-token CamelCase to sentence case, so "MixDoctor" was rendering as "Mixdoctor". Two words ("Mix Doctor") matches every other section name's pattern (Style Profile, Project Setup, Track Layout, Mix & Master Chain, Patch Framework, etc.) and renders cleanly through the existing pipeline. `npm run verify` passes: lint + ~600 unit + build + 44 smoke. Live UI verified against saved run 855f1376… with probes confirming all six fixes: - StickyNav.overflow-x-auto wrapper gone (pills wrap to 2 rows) - Zero Mix Chain order badges in #section-mix-chain - "Target (range)" header present, "Target dB" header gone - "Dense DAW Lab" text absent from page - "Interpretation Model" label absent from page - "Mix Doctor" section title present, "Mixdoctor" absent Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Oz <oz-agent@warp.dev>
5 tasks
slittycode
pushed a commit
that referenced
this pull request
May 18, 2026
PR 6 of 16 in the UI consistency overhaul. Promotes the AnalysisResults page header to <SectionHeader> and wraps the top TEMPO/KEY/METER/ CHARACTER metric strip in a <DeviceRack name="Measurement Summary"> with <MetricTile> cards inside. Stacked on PR #69 (D.4 FileUpload). Will rebase onto main after #69 merges. Header (AnalysisResults.tsx:835-880 → :835-878): - <h1> + Download buttons row → <SectionHeader size="lg" variant="inline" eyebrow="ASA Results" titleRole="page-title"> with both Download buttons in the action slot. - The Activity icon prefix on the h1 disappears — the SectionHeader LED dot already carries the "results are live" semantic. - The subtitle (sourceFileName · interpretationSubtitle) stays as a separate <p> sibling, preserving data-testid="analysis-results- subtitle" and the existing text-role-meta styling. Indented pl-4 to align under the title after the LED. - Download data: <button> → <Button variant="secondary" leadingIcon=FileJson>. Preserved data-testid="analysis-export-json". - Download report: <button> → <Button variant="primary" leadingIcon=FileText>. Preserved data-testid="analysis-export-markdown". Metric strip (AnalysisResults.tsx:884-1012 → :882-1010): - Outer <div className="grid grid-cols-2 md:grid-cols-4 gap-3"> wrapped in <DeviceRack name="Measurement Summary" density="dense" status="success">. The grid layout stays as-is inside the rack body so the responsive 2-col / 4-col break still works. - 4 × AccentMetricCard → 4 × <MetricTile size="lg" accent="accent">. API parity drop-in: • label (was a flex-span with icon+text) → icon prop + label string. Cleaner separation; the MetricTile primitive places the icon in the eyebrow row. • value / unit / headerRight / footer pass through unchanged. • The hand-rolled text-[1.5rem] override on the CHARACTER value disappears — MetricTile size="lg" already renders text-2xl which is the same 1.5rem. Imports: - AccentMetricCard stays imported from ./MeasurementPrimitives. Other usages of it remain at :1249-1258 and :1444-1448 (Patches / Sonic Elements sections — migrated in later PRs D.5b/c/d). - Added Button / DeviceRack / MetricTile / SectionHeader from ./ui. Preserved verbatim: - All data-testid attributes (analysis-results-root, analysis-results- subtitle, analysis-export-json, analysis-export-markdown). - All visible text strings: "Analysis Results", "Download data", "Download report", "TEMPO", "BPM", "KEY SIG", "METER", "CHARACTER", "SCANNING...". - The PhaseSourceBadge + ConfidenceBandBadge + MetricBar + StatusBadge + TokenBadgeList rendering inside each tile is untouched. - Audit Finding #4 commentary preserved (canonical band-pill vocabulary). - Audit Finding #1 ordering comment preserved (MeasurementDashboard rendering at the bottom of the scroll). - The lowConfidenceIndicator + characteristicPills logic untouched. Verified: lint clean, 628/628 unit tests passing, build clean (AnalysisResults chunk 255 KB / 63 KB gz; index chunk 323 KB / 97 KB gz — both stable vs PR 5). https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
slittycode
added a commit
that referenced
this pull request
May 18, 2026
PR 6 of 16 in the UI consistency overhaul. Promotes the AnalysisResults page header to <SectionHeader> and wraps the top TEMPO/KEY/METER/ CHARACTER metric strip in a <DeviceRack name="Measurement Summary"> with <MetricTile> cards inside. Stacked on PR #69 (D.4 FileUpload). Will rebase onto main after #69 merges. Header (AnalysisResults.tsx:835-880 → :835-878): - <h1> + Download buttons row → <SectionHeader size="lg" variant="inline" eyebrow="ASA Results" titleRole="page-title"> with both Download buttons in the action slot. - The Activity icon prefix on the h1 disappears — the SectionHeader LED dot already carries the "results are live" semantic. - The subtitle (sourceFileName · interpretationSubtitle) stays as a separate <p> sibling, preserving data-testid="analysis-results- subtitle" and the existing text-role-meta styling. Indented pl-4 to align under the title after the LED. - Download data: <button> → <Button variant="secondary" leadingIcon=FileJson>. Preserved data-testid="analysis-export-json". - Download report: <button> → <Button variant="primary" leadingIcon=FileText>. Preserved data-testid="analysis-export-markdown". Metric strip (AnalysisResults.tsx:884-1012 → :882-1010): - Outer <div className="grid grid-cols-2 md:grid-cols-4 gap-3"> wrapped in <DeviceRack name="Measurement Summary" density="dense" status="success">. The grid layout stays as-is inside the rack body so the responsive 2-col / 4-col break still works. - 4 × AccentMetricCard → 4 × <MetricTile size="lg" accent="accent">. API parity drop-in: • label (was a flex-span with icon+text) → icon prop + label string. Cleaner separation; the MetricTile primitive places the icon in the eyebrow row. • value / unit / headerRight / footer pass through unchanged. • The hand-rolled text-[1.5rem] override on the CHARACTER value disappears — MetricTile size="lg" already renders text-2xl which is the same 1.5rem. Imports: - AccentMetricCard stays imported from ./MeasurementPrimitives. Other usages of it remain at :1249-1258 and :1444-1448 (Patches / Sonic Elements sections — migrated in later PRs D.5b/c/d). - Added Button / DeviceRack / MetricTile / SectionHeader from ./ui. Preserved verbatim: - All data-testid attributes (analysis-results-root, analysis-results- subtitle, analysis-export-json, analysis-export-markdown). - All visible text strings: "Analysis Results", "Download data", "Download report", "TEMPO", "BPM", "KEY SIG", "METER", "CHARACTER", "SCANNING...". - The PhaseSourceBadge + ConfidenceBandBadge + MetricBar + StatusBadge + TokenBadgeList rendering inside each tile is untouched. - Audit Finding #4 commentary preserved (canonical band-pill vocabulary). - Audit Finding #1 ordering comment preserved (MeasurementDashboard rendering at the bottom of the scroll). - The lowConfidenceIndicator + characteristicPills logic untouched. Verified: lint clean, 628/628 unit tests passing, build clean (AnalysisResults chunk 255 KB / 63 KB gz; index chunk 323 KB / 97 KB gz — both stable vs PR 5). https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe Co-authored-by: Claude <noreply@anthropic.com>
This was referenced May 27, 2026
slittycode
added a commit
that referenced
this pull request
May 31, 2026
…record (#124) * docs: refresh stale items vs current code; archive completed hosting record Sweep against the 2026-05-30 audit findings — the doc surface had drifted through the MT3, Live 12 catalogue, pianoroll/symusic, and recommendation-proof merges. Adds those features to the inventories where they were missing, fixes a wrong line reference, removes a dead commit hash, refreshes date stamps, and archives a completed-work record. - MT3 polyphonic transcription: add to apps/backend/AGENTS.md (file map + guidance), apps/backend/ARCHITECTURE.md (Components table + mt3-transcriptions route), apps/backend/README.md (Polyphonic section now reflects opt-in shipping), apps/ui/AGENTS.md + apps/ui/README.md (mt3Client.ts + UI surface), CLAUDE.md (#19 backend file, #6a frontend service), and all three CHANGELOG.md files. - Live 12 catalogue + phase2_catalogue_gates, transcription pianoroll + symusic, recommendation-proof campaign: add to CHANGELOG.md (root) and apps/backend/CHANGELOG.md and apps/ui/CHANGELOG.md. - CLAUDE.md: fix wrong analysis_runtime.py line ref (685 → ~800), add ASA_ENABLE_MT3 and ASA_SAMPLE_SYNTH_BACKEND env vars, correct the tsconfig excludes description, drop the unreachable 5c40dd4 commit hash. - apps/backend/AGENTS.md + ARCHITECTURE.md: drop the same dead 5c40dd4 hash. - docs/ARCHITECTURE_STRATEGY.md + docs/POLYPHONIC_TRANSCRIPTION_SPIKE.md: reflect that MT3 has shipped as an opt-in, additive stage (the spike harness remains the offline comparison rig for other candidates). - docs/SAMPLE_GENERATION.md: fix the schematic node label (synth_kit() → the real synth_kick/snare/hat() trio). - apps/backend/JSON_SCHEMA.md: annotate the shared-vs-full carve-out so the full-mode-only keys (keyProfile, tuningFrequency, tuningCents, lufsMomentaryMax, lufsShortTermMax, pitchDetail) are explicit per CLAUDE.md tripwire #4. - Archive docs/PUBLIC_HOSTING_FOUNDATION.md → docs/history/public-hosting-foundation-2026-04-01.md with an archived-banner; update inbound references in CHANGELOG.md, docs/SETUP.md, docs/history/README.md, and docs/history/archive/refactor-state-2026-03-18.md. - incorporations/forking-plans-2026-05-14.md: add a 2026-05-30 status update noting Plan 6 substantially landed via the Live 12 catalogue + gates work. No code touched — markdown only. https://claude.ai/code/session_016m6cUFNwb3kD2EzAbgqCHe * docs: fix self-contradictory archive banner phrasing The banner said the "Remaining work" section "lives outside this archived doc" but that section is in the doc itself. Aligns with the clearer phrasing already used in docs/history/README.md. https://claude.ai/code/session_016m6cUFNwb3kD2EzAbgqCHe --------- Co-authored-by: Claude <noreply@anthropic.com>
slittycode
pushed a commit
that referenced
this pull request
Jun 2, 2026
Phase 1 v2 emits truePeak: null for digital silence (no defined dBTP), but the frontend type declared it `number` and several display sites called .toFixed unguarded — a latent crash on silent input. Worse, the HTTP envelope coerced null -> 0.0, falsely reporting a full-scale peak for a silent track (violating measurement-honesty invariants #1/#4). Backend: - server_phase1._build_phase1 now preserves null via _coerce_nullable_number instead of defaulting to 0.0; PLR fallback already guards None. - Tests: truePeak passthrough/null + PLR-null-when-peak-null. Frontend: - Phase1Result.truePeak typed `number | null`. - analysisResultsViewModel: formatTruePeak (renders "—" for null, matching the house formatNumber convention) and masterCeilingDb (falls back to the -0.3 dB cap when there is no peak to duck below); applied at all 6 sites. - MeasurementDashboard: omit the true-peak marker, PLR gap fill, and PLR annotation for silence (LUFS marker still renders). - backendPhase1Client: accept an explicit null truePeak (legacy path) while still rejecting a malformed/missing field. - exportUtils markdown renders "—" for a null peak. - Tests: null-silence card renders "—" without crashing; helper unit cases. Docs: JSON_SCHEMA truePeak note now states null == digital silence. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q
slittycode
added a commit
that referenced
this pull request
Jun 2, 2026
…t PLR (#128) * fix(loudness): Phase 1 v2 - truePeak to dBTP, bpmConfidence to 0-1, coherent PLR Phase 1 schema v2 migration for the loudness fields, motivated by a consumer audit: ~12 UI/recommendation consumers already assumed `truePeak` was dBTP (several were latent bugs), while only the loudness guardrail treated it as the linear amplitude proxy v1 actually emitted. - truePeak now emitted in dBTP (0.0 == full scale, >0 == inter-sample over); null for silence. bpmConfidence normalized to 0-1 (raw Essentia ~0-5.32 / 5). - PLR is now a coherent dB-domain subtraction (truePeak_dBTP - lufsIntegrated), fixing the prior linear-minus-dB unit mismatch, across analyze_core, the HTTP fallback, and the phase1 eval consistency gate. - Add top-level `phase1Version` ("phase1.v2") so consumers can detect the generation; loudness guardrail over-check moves to dBTP (>0); the validator drops its special-case bpmConfidence threshold (now shares the 0.4 hedge). - Re-baseline the golden (truePeak 0.0, plr 5.6); add dedicated analyze_plr and dBTP true-peak unit tests; update fixture expectations, types, JSON_SCHEMA. - ADR 0002 records the v2 bump, superseding ADR 0001 for these two fields. Verified: backend unittest suite green; frontend tsc + 716 unit tests + build green. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * chore: ignore Python bytecode repo-wide The .gitignore only covered apps/backend/__pycache__/, so bytecode generated outside the backend (e.g. scripts/__pycache__/ when build_live12_catalogue.py is imported) showed up as untracked. Generalize the __pycache__/ and *.py[cod] rules to the whole repo. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * fix(loudness): v2 follow-ups - phase1 eval manifest targets + dBTP labels - Backend CI fix: phase1_eval_manifest.json truePeak/plr thresholds still encoded v1 linear values (click_120 truePeak 0.9 / plr 24.8; sine_220 0.5 / 7.4). Migrated to the v2 dBTP measurements (click -0.9 / 23.2; sine -6.0 / 1.0). The plr:consistency gate already passes; only the manifest's expected targets were stale. - Review (#128): three analysisResultsViewModel.ts display sites labeled the now-correct truePeak value as "dB"; corrected to "dBTP". Verified: tests.test_phase1_evaluation green; frontend tsc + 716 unit green. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * Harden nullable truePeak across the v2 contract Phase 1 v2 emits truePeak: null for digital silence (no defined dBTP), but the frontend type declared it `number` and several display sites called .toFixed unguarded — a latent crash on silent input. Worse, the HTTP envelope coerced null -> 0.0, falsely reporting a full-scale peak for a silent track (violating measurement-honesty invariants #1/#4). Backend: - server_phase1._build_phase1 now preserves null via _coerce_nullable_number instead of defaulting to 0.0; PLR fallback already guards None. - Tests: truePeak passthrough/null + PLR-null-when-peak-null. Frontend: - Phase1Result.truePeak typed `number | null`. - analysisResultsViewModel: formatTruePeak (renders "—" for null, matching the house formatNumber convention) and masterCeilingDb (falls back to the -0.3 dB cap when there is no peak to duck below); applied at all 6 sites. - MeasurementDashboard: omit the true-peak marker, PLR gap fill, and PLR annotation for silence (LUFS marker still renders). - backendPhase1Client: accept an explicit null truePeak (legacy path) while still rejecting a malformed/missing field. - exportUtils markdown renders "—" for a null peak. - Tests: null-silence card renders "—" without crashing; helper unit cases. Docs: JSON_SCHEMA truePeak note now states null == digital silence. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q --------- Co-authored-by: Claude <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jun 2, 2026
* WS3a: asa-dsp ↔ Essentia loudness parity harness + report Before the loudness-spectro-wasm core (asa-dsp, openmeters-derived BS.1770) can back any ASA product path (WS3b backend, WS3c browser readout), it has to agree with Essentia — ASA's authoritative Phase 1 loudness source, since Phase 1 measurements are ground truth (PURPOSE.md invariant #1). scripts/essentia_parity.py runs a deterministic synthetic corpus (tones, sweep, white/pink noise, decorrelated stereo) through BOTH paths at each file's native rate (no resampling): - asa-dsp via the native measure-cli binary (source-identical to the WASM core, so WS3a needs no wasm toolchain), and - Essentia via AudioLoader -> LoudnessEBUR128, mirroring analyze_core.analyze_loudness, plus a dBTP true peak from TruePeakDetector. Primary gate: integrated LUFS within +/-0.1 LU on every file. Result (docs/essentia-parity-report.md): 7/8 signals agree within +/-0.02 LU (most within +/-0.002) — essentially perfect BS.1770 parity on tones and noise. The sole breach is the 20 Hz->20 kHz log sweep at -0.102 LU (0.002 over the gate), a pathological non-program signal. Verdict is reported as the harness computed it (FAIL by that single borderline case); the realistic-signal agreement is the substantive finding. Surfaced at the WS3a hard checkpoint. README documents the harness as Validation helper #4. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * WS3a review: simplify run(), flag true-peak divergence for WS3c Addresses the PR #129 review: - run() no longer calls build_report() and discards the text; it returns rows only. build_report() now runs exactly once, in main(). Pure refactor — the harness reproduces identical numbers. - Report caveats now explicitly flag the broadband true-peak divergence (white_noise ~0.6 dBTP gap): WS3c must not expose a browser true-peak readout without its own gate. Integrated loudness is unaffected. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q --------- Co-authored-by: Claude <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jun 2, 2026
…meters (#130) * WS4: patchSmith — downloadable Vital (.vital) presets with cited params Turns measured synthesis character into a loadable Vital preset where every parameter cites the Phase 1 measurement behind it (PURPOSE.md invariant #2) and weak/absent evidence is disclosed rather than guessed (invariant #4). apps/ui/src/services/patchSmith.ts (pure, deterministic): - buildPatch(phase1) maps measurements -> Vital controls, each with a PatchParameterCitation {label, vitalParam, value, display, phase1Fields, rationale, confidence}: * supersawDetail.voiceCount/avgDetuneCents -> osc_1 unison voices + detune * spectralBalance.subBass (strong) -> octave-down osc_2 sub layer * acidDetail.* + spectralBalance brightness -> resonant filter + cutoff * bassDetail.averageDecayMs/type -> amp envelope decay/sustain No measurement -> no bound parameter. Low-confidence/absent sources are skipped or defaulted with an explicit hedge; overall confidence = worst band. - serializeVital(): override-only settings JSON. Confirmed against Vital's load_save.cpp that missing wavetables/modulations/lfos arrays load on init defaults (no fragile base64 wavetable embedding). Unit mappers encode Vital's real ValueDetails (env seconds = value^4; cutoff semitone = 69+12log2(f/440)). apps/ui/src/components/PatchSmithPanel.tsx: a DeviceRack "Generate Vital patch" panel (mounted in AnalysisResults) rendering the cited-parameter manifest, per-param + overall confidence pills, hedges, and the .vital download. Tests (14): deterministic mapping, citation provenance, hedging on low-confidence/empty inputs, the unit mappers, and a .vital re-parse asserting the JSON shape + in-range control values (the machine-verifiable gate). https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q * patchSmith review: cite sub-osc values, drop redundant download button Addresses the PR #130 review: - mapSubLayer now routes osc_2_transpose (−1 octave) and osc_2_level through applyCitation with spectralBalance.subBass provenance, so every osc_2 value in the preset appears in the manifest (invariant #2 completeness) rather than only osc_2_on. Test asserts the new citations. - Removed the redundant header "Download .vital" link from PatchSmithPanel; the body primary CTA is the single download affordance. https://claude.ai/code/session_01Wq1vE1D7o3W9xZKSHXz43Q --------- Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Jun 2, 2026
slittycode
added a commit
that referenced
this pull request
Jun 5, 2026
…#143) Phase 2 emits device recommendations across three free-shaped arrays (abletonRecommendations, mixAndMasterChain, secretSauce.workflowSteps) with free-text values and no machine-checkable freeze. This adds a normalized, versioned, schema-validated contract for that surface. - schemas/recommendations.v1.schema.json: Draft 2020-12 schema. Envelope {version, recommendations[]}; each entry {device, parameter, value, unit, range, cited_measurements[]}, additionalProperties:false, cited_measurements minItems:1. - recommendations_contract.py: deterministic projection of the three Phase 2 card arrays into the normalized shape, validated against the literal schema file via jsonschema (not a hand-rolled mirror — avoids the drift ADR 0001 warned about). Derived/additive: never overrides Phase 1 (invariant #1); admits only cited cards (invariant #2); unit/range are nullable best-effort since the static Live 12 catalogue carries no min/max (invariant #4). - server.py: attaches the validated envelope to the producer_summary interpretation result; it travels into GET /api/analysis-runs/{run_id} under stages.interpretation.result.recommendations (degrades to absent on error). - interpretation.ts: TS mirror (RecommendationsContract). - ADR 0003 + CLAUDE.md index document the contract and its semver/compat policy. Tests: test_recommendations_contract.py (schema validity, projection-validates, round-trip, freeze), a server wiring test, and a runtime snapshot-survival test. Full backend suite green (1153), tsc --noEmit green. jsonschema==4.26.0 pinned. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jun 10, 2026
…a local Claude Code CLI (#153) * docs: fix command accuracy in CLAUDE.md and AGENTS.md - Fix unittest slash-path form → dotted module form in apps/backend/AGENTS.md - Add UPDATE_PHASE1_GOLDEN re-baseline command to CLAUDE.md Tripwire #4 - Add first-time Playwright install line to CLAUDE.md frontend commands - Add record_artifact pointer to analysis_runtime.py entry in CLAUDE.md - Add --source/--output invocation to build_live12_catalogue.py entry - Reconcile Node version in apps/ui/AGENTS.md against package.json (no engines field) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(phase2): add 'claude' provider — text-only interpreter via local Claude Code CLI ASA_PHASE2_PROVIDER=claude routes producer_summary interpretation to ClaudeCliProvider: runs the operator's local Claude Code CLI headless (--safe-mode, --tools "", --no-session-persistence), prompt via stdin, profile responseSchema converted from Gemini dialect and enforced via --json-schema. Text-only by design — grounds on the prompt's embedded Phase 1 JSON; audio is never sent; no GEMINI_API_KEY needed. Output flows through the identical parse/citation/catalogue/recommendations.v1 tail. Default-off; gemini stays the product default (mirrors the MOSS pattern). Live verification on a real track (no Gemini key): 26/26 cards cited, 0 invented citation paths, recommendations.v1 envelope 26 entries, full-surface coverage; 6 warn-and-keep catalogue annotations. Also adds audits/owner-assessment-2026-06-10.md (whole-project audit write-up). Tests: tests.test_phase2_provider 42/42; tests.test_server 224/224 (Gemini path behavior-preserved); full backend discover green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: skip Claude Code Review job cleanly when CLAUDE_CODE_OAUTH_TOKEN is unset The advisory review action has failed every PR since 2026-06-09 in ~25s because the repo secret resolves empty. Gate the steps on secret presence so a missing credential yields a passing job with skipped steps instead of a red check. Re-add the secret in Settings → Secrets → Actions to restore actual reviews. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(audit): add DJ Metatron batch result — 56/56 cited across both real tracks Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: restore claude-code-review.yml byte-identical to main The claude-code-action token exchange validates that the workflow file on a PR branch matches the default branch exactly; any divergence (or the file's absence on pre-workflow branches — the actual cause of the 2026-06-09+ failures) yields 401 'Workflow validation failed'. The skip guard added in f6d61e5 therefore could never help and itself tripped the validation; reverting to main's exact content lets the review run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(phase2): make provider policy explicit — selectable, mutually exclusive, no fallback/ensemble; Gemini stays default; claude is local-CLI-only Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jun 10, 2026
- Fix unittest slash-path form → dotted module form in apps/backend/AGENTS.md - Add UPDATE_PHASE1_GOLDEN re-baseline command to CLAUDE.md Tripwire #4 - Add first-time Playwright install line to CLAUDE.md frontend commands - Add record_artifact pointer to analysis_runtime.py entry in CLAUDE.md - Add --source/--output invocation to build_live12_catalogue.py entry - Reconcile Node version in apps/ui/AGENTS.md against package.json (no engines field) Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jul 4, 2026
#204) Behavior-neutral: analyze_time_signature computed per-candidate accent evidence (dominance + per-bar-position onset means for bar lengths 3-7) and threw it away. Now it rides the payload as an additive, full-mode-only top-level field, strongest candidate first, empty on every fallback branch: timeSignatureCandidates: [{timeSignature, dominance, positionMeans[]}] fundamentalsQuality.domains.meter.evidence gains {bestCandidate, margin, candidateCount} derived from it, so the trust layer can say HOW ambiguous the meter read was rather than just that it was ambiguous. This is the evidence surface the meter improvement track (accuracy program Phases B/C — the measured weak layer) builds on; downstream must never treat a candidate as an override of timeSignature (invariant #1). Contract mirrors (tripwire #4): JSON_SCHEMA.md inventory + full-only list + field table; server_phase1._build_phase1 forwarding (caught by the golden's forwarding guard); src/types/measurement.ts + backendPhase1Client.ts reconstructor (drops malformed entries); phase1FullPayload fixture; golden re-baselined (new key, structural). Tests: meter-evidence summary (backend), parser filtering (Vitest), full-mode key set. Backend suite 1295 OK; fundamentals gate 15/15; frontend verify green (877+ unit, 51 smoke). Claude-Session: https://claude.ai/code/session_01T4wfz87k6kzJqkKLKZE7YL Co-authored-by: Claude <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jul 4, 2026
Replaces the loudness-interval swing proxy (grooveDetail.kickSwing/
hihatSwing) with a real micro-timing measurement. compute_swing_detail
reads swing from the long/short alternation of 8th-note inter-onset
intervals: swingPercent = long/(long+short)*100 on Ableton's Groove Pool
50-75 scale (50 straight, 66.7 full triplet), so it drops straight into a
groove template. Additive rhythmDetail.swingDetail {swingPercent,
swingConfidence, gridResolution, direction, meanAbsOffsetMs,
offbeatOnsetCount}; reuses the onset array already computed in
analyze_rhythm_detail (no extra DSP pass); null when unmeasurable.
Interval-ratio rather than phase-vs-grid is deliberate: on sparse
material the beat tracker sometimes anchors 'the beat' on a loud offbeat
hat, which flips a phase reading (58% -> 42%). The long/short structure
is invariant to that choice — validated by a phase-shift-invariance test
and against the corpus swing clips (truth 50/54/58/62/66 -> measured
50.0/54.8/57.1/61.9/65.9, all within +-1.3, well inside the +-3 gate).
Corpus: swing clips now render a realistic swung hat pattern (straight
on-beat 8th + delayed off-beat 8th) so the interval alternation exists;
swingPercent promoted from manifest truth to an active +-3.0 check on all
5 swing clips (fundamentals_evaluation learns expected.swingPercent).
UI: reconstructionBrief groove sentence prefers the measured percentage
('swings at about 62% - set the Groove Pool swing to match'), falls back
to the proxy; MeasurementDashboard Swing panel shows the Groove-Pool
number.
Contract mirrors (tripwire #4): JSON_SCHEMA.md swingDetail sub-fields;
types/measurement.ts SwingDetail (rhythmDetail passes through wholesale,
no reconstructor); phase1FullPayload fixture; golden re-baselined
(nested, structural). Tests: test_swing_detail (ratios, phase-flip
invariance, degenerate), brief swing-sentence, corpus expected-keys.
Backend 1299 OK, fundamentals gate green, frontend verify green.
Claude-Session: https://claude.ai/code/session_01T4wfz87k6kzJqkKLKZE7YL
Co-authored-by: Claude <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jul 4, 2026
) analyze_key now runs KeyExtractor for edma + temperley + krumhansl in full mode and emits an additive, full-only keyEnsemble field: {method:'profile_vote.v1', agreement, profiles[], alternates[]}. SURFACING-ONLY: the shipped key/keyConfidence/keyProfile are unchanged — still EDMA (golden diff confirms only keyEnsemble was added; the key value is byte-identical). The vote is evidence until the GiantSteps Key gate proves it beats EDMA-alone. Pre-registered decision rule (frozen before the run) in incorporations/key-ensemble-decision-2026-07-04.md: adopt iff mirexWeighted gain >= +0.02 AND no exact-rate regression beyond 0.01, on >= 400 evaluable clips. agreement (0-3) is a ready confidence calibrator; alternates hedge the relative-major/minor error that is EDMA's typical miss. Gated on full mode (like tuning*): fast mode is unchanged and pays no extra KeyExtractor cost. Any/all profile failures degrade gracefully (null ensemble; EDMA key still ships). Contract mirrors (tripwire #4): analyze.py + server_phase1 forwarding, JSON_SCHEMA.md inventory/full-only/field-row, test_audio_fixture full-mode key set, types/measurement.ts KeyEnsemble, backendPhase1Client forwarding, phase1FullPayload fixture, golden re-baselined (new key, structural). Tests: test_key_ensemble (agreement/alternates, full-vs-fast, all-fail). Backend 1302 OK, fundamentals gate green, frontend verify green. Claude-Session: https://claude.ai/code/session_01T4wfz87k6kzJqkKLKZE7YL Co-authored-by: Claude <noreply@anthropic.com>
slittycode
added a commit
that referenced
this pull request
Jul 12, 2026
) Runs chord/key chroma on a bass-removed stem mix (other+vocals) instead of the full mix when Demucs stems are available. The bassline is the single biggest polluter of full-mix chroma, so harmonic-source isolation is the highest-leverage chord-accuracy lever in the baseline audit. - mix_stems_mono(stems, ('other','vocals')) in analyze_audio_io.py sums the harmonic stems to mono; None when no stem loads. - analyze_chords / analyze_segment_key gain a harmonic_mono kwarg; when present the chroma runs on it. Additive chordDetail.chordSource + segmentKey[].source ('full_mix' | 'harmonic_stems') record which ran. - Stems absent -> full-mix path, BIT-IDENTICAL to before (golden diff adds only the two source keys; chord values unchanged; chords-only corpus clips hold at ~0.99). Validation honesty: Demucs model weights are network-blocked in this sandbox (proxy 403, same restriction as the datasets), so separation returns None here and the synthetic multi-layer clips exercise the full-mix fallback, not the harmonic path. The harmonic wiring is proven by unit tests (mix_stems_mono summing/skip-missing; chordSource + segmentKey source tagging); the end-to-end accuracy GAIN must be measured where Demucs weights are reachable (operator machine / CI with network) on real dense mixes — the synthetic bass is a clean low sine that barely pollutes chroma anyway. No accuracy improvement is claimed from this sandbox. Contract mirrors (tripwire #4): JSON_SCHEMA rows; types/measurement.ts ChordDetail.chordSource + SegmentKeyEntry.source; backendPhase1Client chord reconstructor forwards chordSource (segmentKey passes through); phase1FullPayload fixture; golden re-baselined (structural). Tests: test_stem_aware_chords. Backend 1306 OK, fundamentals gate green, frontend verify green. Claude-Session: https://claude.ai/code/session_01T4wfz87k6kzJqkKLKZE7YL Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Jul 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
recover_incomplete_attempts()was marking interrupted measurement and symbolic jobs back toqueued, causing heavy ML subprocesses (torch + Demucs + torchcrepe) to fire on every server restart. Now marks theminterruptedto match the existing interpretation stage behaviour..runtime.backup.*directories andvenv.broken.*that were inadvertently committed (~5.7 GB of test artifacts and stale data).Test plan
_demucs_chunked_inferencetested on 15s clip and full 5-min FLAC — both produce valid stemspython -m unittest discover -s tests(176 tests, 0 failures)test_recover_interrupted_attemptsupdated to assertinterruptedfor all three stage types🤖 Generated with Claude Code